package com.oyesgo.statistics.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.oyesgo.statistics.log.LogUtils;
import com.oyesgo.statistics.model.LogInfo;
import com.oyesgo.statistics.regex.RegexHelp;
import com.oyesgo.statistics.service.LogInfoService;
import com.oyesgo.statistics.thread.CheckThread;
import com.oyesgo.statistics.thread.ToDBThread;
import com.oyesgo.statistics.time.TimeSercice;
import com.oyesgo.statistics.utils.DateFormat;
import com.oyesgo.statistics.utils.DateUtils;
import com.oyesgo.statistics.utils.GlobalContent;
import com.oyesgo.statistics.utils.MailFactory;
import com.oyesgo.statistics.utils.StringUtils;

/**
 * @FileName LogInfoServiceImpl.java
 * @Description: 日志服务实现类
 * @author rose
 * @version 1.0
 * <p>Company: OYESGO</p>
 * <p>Copyright: Copyright (c) 2010</p>
 */
public class LogInfoServiceImpl implements LogInfoService{
	public static final String BLANK = " ";
	/**
	 * 取cookie的正则
	 */
	public static final String REGEX_COOKIE = "%23.{20}[\"|;]";
	/**
	 * 取频道类型的正则
	 */
	public static final String REGEX_CHANNEL_TYPE = "http://.+?.oyesgo";
	
	/**
	 * 推广源的urlkey正则
	 */
	public static final String REGEX_REF_KEY = "ref=.{1,32}?&|ref=.{1,32}";
	
	//判定页面类型匹配字符串
	public static final String JSP_TYPE = ".jsp";
	public static final String PHP_TYPE = ".php";
	public static final String HTML_TYPE = ".html";
	/**
	 * 取前两天的日志
	 */
	public static final int N = -2;
	
//	/**
//	 * 推广源的url正则
//	 */
//	public static final String REGEX_FROM_URL = "url=.{1,32}";
	 
	public void insertLogInfo() {
		File file = new File(MailFactory.SEVEER_FILE_PATH_START+DateUtils.getYesterdayByType(MailFactory.SEVEER_FILE_DATE_TYPE,N)+MailFactory.SEVEER_FILE_PATH_END);
		LogUtils.printlnToFile("[开始统计日志] [" + MailFactory.SEVEER_FILE_PATH_START+DateUtils.getYesterdayByType(MailFactory.SEVEER_FILE_DATE_TYPE,N)+MailFactory.SEVEER_FILE_PATH_END+"]");
		FileInputStream fis = null;
		InputStreamReader isr = null;
		LineNumberReader lnr = null;
		try {
			fis = new FileInputStream(file);
			isr = new InputStreamReader(fis);
			lnr = new LineNumberReader(isr);//创建套接字按行读取
			
			//声明和创建一些临时变量
			String tempStr = null;
			String[] strs = null;			//取一般信息用到的分割结果对象
			String[] strsForCookie = null; //取cookie时候用到的分割结果对象
			String strs6 = "";
			int k = 0; //临时变量 用于计算开启线程的数目
			int a = 1; //用于记录线程数
			String cookie = "";
			String channelType = "";
			String fromUrl = "";
			String refKey = "";
			int pageType = 0;
			ThreadGroup tg = new ThreadGroup("checkdbthread");//建立一个线程组用于封装所有线程监控实时状态
			Date startTime = new Date();
			List<LogInfo> logInfoList = new ArrayList<LogInfo>();//创建一个集合包装LogInfo
			try {
				//按照日志model类开始封装
				while((tempStr = lnr.readLine()) != null){
					//按照空格截取一条字符串
					strs = StringUtils.splitByTag(tempStr,BLANK);
					//如果数组长度大于7做如下逻辑 否则什么都不做
					if(strs.length > 7){
						strs6 = strs[6];
						//如果请求不包含如下信息 则开始进行字符解析封装 入库操作
						if(!(RegexHelp.validate(".css", strs6) || RegexHelp.validate(".js", strs6) ||
							 RegexHelp.validate(".txt", strs6) || RegexHelp.validate(".png", strs6)|| 
							 RegexHelp.validate("log.jsp", strs6)|| RegexHelp.validate(".gif", strs6) ||
							 RegexHelp.validate(".jpg", strs6) || RegexHelp.validate(".ico", strs6) )) {
							 
							 //开始做封装带有LogInfo的集合 start
							 LogInfo logInfo = new LogInfo();
							 logInfo.setUserIp(strs[0]);//设定访问ip
							 logInfo.setVisitTime(DateFormat.getDateByFormat(strs[3].replace("[", "")));//设定访问时间
							 logInfo.setVisitUrl(strs[6].replace("\"","").length() > 128 ? strs[6].replace("\"","").substring(0,128) : strs[6].replace("\"",""));//设定访问链接
							 logInfo.setVisitState(new Integer(strs[8]));//设定状态码
							 //开始做取cookie分割
							 strsForCookie = StringUtils.splitByTag(tempStr, " \"");
							 //设定浏览器信息
							 logInfo.setUserAgent(strsForCookie[3].replace("\"", "").length() > 256 ? strsForCookie[3].replace("\"", "").substring(0,256) : strsForCookie[3].replace("\"", ""));
							 //匹配cookie 设定cookie
							 cookie = RegexHelp.search(REGEX_COOKIE, strsForCookie[4]).replace("%23", "").replace("\"", "").replace(";", "");
							 if("".equals(cookie)){
								 cookie = "_";
							 }
							 logInfo.setCookie(cookie);
							 //设定频道类型
							 channelType = RegexHelp.search(REGEX_CHANNEL_TYPE,logInfo.getVisitUrl()).replace("http://", "").replace(".oyesgo", "");
							 logInfo.setChannelType(channelType);
							 //设定refkey
							 refKey = RegexHelp.search(REGEX_REF_KEY, logInfo.getVisitUrl());
							 logInfo.setRefKey(refKey.replace("ref=", "").replace("&", ""));
							 //设定fromurl
							 fromUrl = logInfo.getRefKey();
							 logInfo.setFromUrl(fromUrl);
							 //设定页面类型
							 String visitUrl = logInfo.getVisitUrl();
							 if(visitUrl != null && !"".equals(visitUrl)){
								 if(visitUrl.indexOf(JSP_TYPE) != -1) pageType = 1; //jsp
								 else if (visitUrl.indexOf(PHP_TYPE) != -1) pageType = 3; //php
								 else if (visitUrl.indexOf(HTML_TYPE) != -1) pageType = 4; //html
								 else pageType = 2; //servlet or 首页
							 }
							 logInfo.setPageType(pageType);
							 k++;
							 logInfoList.add(logInfo);
							 //开始做封装带有LogInfo的集合  end
//							 System.out.println(logInfo);
//							 System.out.println(k+":"+tempStr);
							 //有3w条开启一个线程入库
							 if(k % 30000 == 0){
								 ToDBThread toDBThread = new ToDBThread(a,logInfoList);//创建线程放入线程组
								 Thread thread = new Thread(tg,toDBThread);
								 thread.setDaemon(true);//设定问守护线程 当主线程结束则结束
								 thread.start();//开启线程 开始入库
								 
								 logInfoList = new ArrayList<LogInfo>();//再次创建list
								 a++;
								 try {
									 Thread.sleep(5000);//3w记录休息5秒 此处防止内存溢出
								 } catch (InterruptedException e) {
									 e.printStackTrace();
								 }
							 }
						}
					}
					//做检测 有数据发生异常立即结束主线程 这样所有子线程也会结束 因为都是守护线程
					if(GlobalContent.flag){
						LogUtils.printlnToFile("[主线程结束]");
						CheckThread.sendMailToRose();//发送邮件
						return;
					}
				}
				//入库剩余小于3w条数据
				ToDBThread toDBThread = new ToDBThread(a,logInfoList);//创建线程放入线程组
				Thread thread = new Thread(tg,toDBThread);
				thread.setDaemon(true);//设定守护线程
				thread.start();//开启最后一个线程
				
				//监控线程开始
				CheckThread checkThread = new CheckThread(tg,startTime,k);//开启检测线程 监控所有线程结束计算时间 用于性能比较
				checkThread.start();//开始监控所有线程
			} catch (IOException e) {
				LogUtils.printlnToFile("[按行读取失败!]");
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			LogUtils.printlnToFile("[文件路径不正确!]");
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		LogInfoService logInfoService = new LogInfoServiceImpl();
		logInfoService.insertLogInfo();//本次执行
		TimeSercice timeSercice = new TimeSercice();
		timeSercice.execute();//执行定时器 下一次执行时第二天的7点 以后每隔一天执行一次
	}
}
